/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.google.code.twiddling.core.io.jline;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import jline.ConsoleOperations;
import jline.ConsoleReader;
import jline.History;
import jline.SimpleCompletor;
import jline.Terminal;
import jline.UnixTerminal;

import com.google.code.twiddling.core.bootstrap.TwiddlingShell;
import com.google.code.twiddling.core.context.Environment;
import com.google.code.twiddling.core.io.CommandListener;
import com.google.code.twiddling.core.io.IO;
import com.google.code.twiddling.core.io.IOManager;
import com.google.code.twiddling.core.io.SystemIO;
import com.google.code.twiddling.core.layout.LayoutManager;
import com.google.code.twiddling.core.log.LogUtils;

/**
 * 
 * @author <a href="mailto:jeff.yuchang@gmail.com">Jeff Yu</a>
 *
 */
public class JLineIOManager extends SystemIO implements IOManager {
	
	private static Logger logger = LogUtils.getL7dLogger(JLineIOManager.class);
	
	private ConsoleReader reader;	
	private CommandListener listener;
	private Environment environment;
	private ProcessThread processThread;
	
	private boolean started = false;
	
	public JLineIOManager(Environment environment) {
		super();
		this.environment = environment;
		
		try {
			Terminal terminal = new UnixTerminal();
			terminal.enableEcho();
			reader = new ConsoleReader(inputStream, out, terminal.getDefaultBindings(), terminal);
			reader.addCompletor(new SimpleCompletor(new String[] { "foo", "bar", "baz" }));
			
			History history = new History();
			history.setHistoryFile(new File("/tmp/history.txt"));
			reader.setHistory(history);
			//TODO: add some others reader properties setting for JLineConsole
		} catch (IOException e) {
			logger.log(Level.SEVERE, e.getMessage(), e);
		}
	}
	
	
	public IO getIO() {
		return this;
	}

	
	public void registerCommandListener(CommandListener commandListener) {
		this.listener = commandListener;
	}

	
	public void start() {
		if (started) {
			return;
		}
		processThread = new ProcessThread();
		processThread.start();
		showBanner();
		showProfile();
		
		started = true;
	}

	
	public void stop() {
		if (!started) { 
			return;
		}
		reader = null;
		processThread.shutdown();
		try {
			processThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		processThread = null;
		started = false;
	}
	
	
	private void showBanner() {
		out.println("");
		out.println("");
		out.println("");
			
		LayoutManager.getBannerLayout().setBanner(TwiddlingShell.BANNER)
									   .setVersion(TwiddlingShell.VERSION)
									   .setHomepage(TwiddlingShell.HOMEPAGE)
									   .print(this.out);
		out.println("");
		out.flush();
	}
	
	private void showProfile() {
		out.print(environment.getProfile().toString());
		out.flush();
	}
	
	private class ProcessThread extends Thread {
		
		private JLineIOManager manager = JLineIOManager.this;
		
		private boolean shutdown;
		
		public ProcessThread() {
			super("JLineProcessThread");
			shutdown = false;
		}
		
		public void shutdown() {
			shutdown = true;
		}
		
		public void run() {
			while (!shutdown) {
				String cmd = null;
				try {
					cmd = manager.getReader().readLine();
				} catch (IOException e) {
					e.printStackTrace();
					logger.log(Level.SEVERE, e.getMessage(), e);
				}
				if (cmd != null) {
					cmd = cmd.trim();
					manager.getCommandListener().process(cmd);
					if (!"exit".equals(cmd)) {
						manager.showProfile();
					}
				}
			}
		}
	}

	public ConsoleReader getReader() {
		return reader;
	}
	
	public CommandListener getCommandListener() {
		return this.listener;
	}


	public void print(String string) {
		out.print(string);
		out.flush();
	}


	public void println(String string) {
		out.println(string);
		out.flush();
	}

}
